#include <stdio.h>
#include <stdlib.h>
#include <argp.h>
#include <arpa/inet.h>
#include <linux/types.h>
#include <string.h>
#include <sys/types.h>
//#include <linux/in.h>
//#include <linux/in6.h>

#include "socs/comm/config.h"
#include "socs/comm/types.h"
#include "socs/usr/str_utils.h"
#include "socs/usr/nl_user.h"
#include "socs/comm/socs.h"
#include "LICENSE.h"

const char *argp_program_version = SOCS_VERSION_STR;
const char *argp_program_bug_address = "<develop@reyzar.com>";

#define PREFIX6_FORMAT "ADDR6/NUM"

static struct argp_option options[] = {
	{ NULL, 0, NULL, 0, "options:", 10},
	{ "show",		ARGP_SHOW,		NULL, 0, "The command will show ." },
#if 0
	{
		"csdomain",	ARGP_CSDOMAIN,	"string", 0,
		"set csdomain,the domain of SOCS ,all nodes in a SOCS need the same csdomain.and the max of it's len is 40 byte"
	},
	{
		"hostname",		ARGP_HOSTNAME, "string", 0,
		"set hostname,all nodes in a SOCS need the difference hostname.and the max of it's len is 30 byte"
	},
	{ "tmhello",	ARGP_TMHELLO,   "NUM", 0, "time interval to send hello_basic ,sec." },
	{ "tmdead",		ARGP_TMDEAD,	"NUM", 0, "the expire time of neighbour, sec." },
	{ "tmstable",	ARGP_TMSTABLE,	"NUM", 0, "The stable time of a node ,sec." },
	{ "mcip4",		ARGP_MCIP4,	"ADDR4", 0, "the  ipv4 address of multicast group , all nodes need to join." },
	{ "mcip6",		ARGP_MCIP6,	"ADDR6", 0, "the  ipv6 address of multicast group , all nodes need to join." },
#endif
	{ "saip",		ARGP_SAIP,	NULL, 0, "show the saIP table ." },
#if 0
	{ "saip4",		ARGP_SAIP4,	"ADDR4", 0, "the  saIp4 ." },
	{ "saip6",		ARGP_SAIP6,	"ADDR6", 0, "the  saIp6 ." },
#endif
	{ "neigh",      ARGP_NEIGH, NULL, 0, "show the neigh table ." },
	{ "pool6",		ARGP_POOL6, NULL, 0, "The command will operate on the CPE IPv6 prefix pool." },

	{ NULL, 0, NULL, 0, "Operations:", 3 },
	{ "display",	ARGP_DISPLAY, NULL, 0, "Print the target (default)." },
	{ "add",		ARGP_ADD, NULL, 0, "Add an element to the target." },
	{ "remove",		ARGP_REMOVE, NULL, 0, "Remove an element from the target." },
	{ "flush",		ARGP_FLUSH, NULL, 0, "Remove All elements from the targe." },

	{ NULL, 0, NULL, 0, "Deprecated options:", 7 },
	{
		"prefix", ARGP_PREFIX, PREFIX6_FORMAT, 0, "Prefix to be added to or removed from "
		"the IPv6 pool. You no longer need to name this."
	},
	{ NULL },
};

/*
 * PARSER. Field 2 in ARGP.
 */
static int parse_opt(int key, char *arg, struct argp_state *state)
{
	struct arguments *arguments = state->input;
	int error = 0;

//	printf("parse_opt arg:%s\n",arg);
	switch (key) {
	case ARGP_SHOW:
		arguments->key = ARGP_SHOW;
		break;

	case ARGP_SAIP:
		arguments->key = ARGP_SAIP;
		break;

	case ARGP_NEIGH:
		arguments->key = ARGP_NEIGH;
		break;

	case ARGP_POOL6:
		arguments->key = ARGP_POOL6;
		break;

	case ARGP_PREFIX:
		error = str_to_ipv6_prefix(arg, &arguments->prefix6);
		break;

	case ARGP_DISPLAY:
		arguments->operation = ARGP_DISPLAY;
		break;

	case ARGP_ADD:
		arguments->operation = ARGP_ADD;
		break;

	case ARGP_REMOVE:
		arguments->operation = ARGP_REMOVE;
		break;

	case ARGP_FLUSH:
		arguments->operation = ARGP_FLUSH;
		break;

#if 0

	case ARGP_CSDOMAIN:
		arguments->key = ARGP_CSDOMAIN;
		memcpy(arguments->csDomain, arg, strlen(arg));
		break;

	case ARGP_HOSTNAME:
		arguments->key = ARGP_HOSTNAME;
		memcpy(arguments->hostname, arg, strlen(arg));
		break;

	case ARGP_TMHELLO:
		arguments->key = ARGP_TMHELLO;
		error = str_to_u32(arg, &temp, 0, 0xFFFFFFFF);
		arguments->tmHello = temp * 1000;
		break;

	case ARGP_TMDEAD:
		arguments->key = ARGP_TMDEAD;
		error = str_to_u32(arg, &temp, 0, 0xFFFFFFFF);
		arguments->tmDead = temp * 1000;
		break;

	case ARGP_TMSTABLE:
		arguments->key = ARGP_TMSTABLE;
		error = str_to_u32(arg, &temp, 0, 0xFFFFFFFF);
		arguments->tmStable = temp * 1000;
		break;

	case ARGP_MCIP4:
		arguments->key = ARGP_MCIP4;
		error = str_to_addr4(arg, &arguments->mcIP4);
		break;

	case ARGP_MCIP6:
		arguments->key = ARGP_MCIP6;
		error = str_to_addr6(arg, &arguments->mcIP6);
		break;

	case ARGP_SAIP4:
//		printf("ARGP_SAIP4\n");
		arguments->key = ARGP_SAIP4;
		error = str_to_addr4(arg, &arguments->saIP4);
		break;

	case ARGP_SAIP6:
		arguments->key = ARGP_SAIP6;
		error = str_to_addr6(arg, &arguments->saIP6);
		break;

	case ARGP_ADD:
//		printf("ARGP_ADD\n");
		arguments->operation = ARGP_ADD;
		break;

	case ARGP_REMOVE:
		arguments->operation = ARGP_REMOVE;
		break;
#endif

	default:
		return ARGP_ERR_UNKNOWN;
	}

	return error;
}

/*
 * ARGS_DOC. Field 3 in ARGP.
 * A description of the non-option command-line arguments we accept.
 */
static char args_doc[] = "";

/*
 * DOC. Field 4 in ARGP.
 * Program documentation.
 */
static char doc[] = "socs--the socs module  configuration interface.\v";


/**
 * Uses argp.h to read the parameters from the user, validates them, and returns the result as a
 * structure.
 */
static struct argp argp = { options, parse_opt, args_doc, doc };



/*
 * The main function.
 */
int main(int argc, char **argv)
{
	struct arguments args;
	int error = 0;
//	char buf[100]={};
	error = CheckMachine();

	if (error < 0) {
		printf("License Invalid! Please contact the software distributor (www.reyzar.com)! (code=%x)\n",
			   error);
		return error;
	}

	error = nl_init();

	if (error != 0) {
		goto error;
	}

	memset(&args, 0, sizeof(struct arguments));
	error = argp_parse(&argp, argc, argv, 0, NULL, &args);

	if (error != 0) {
		goto error;
	}

	switch (args.key) {
	case ARGP_POOL6:
		switch (args.operation) {
		case ARGP_DISPLAY:
		case ARGP_ADD:
		case ARGP_REMOVE:
		case ARGP_FLUSH:
			break;
		default:
			printf("Unknown operation for IPv6 pool mode: %u.\n", args.operation);
			return -EINVAL;
		}
		break;
	}
	error = send_to_kernel(&args);

	if (error != 0) {
		goto error;
	}

	recv_from_kernel();
	nl_destroy();
	return 0;
error:
	nl_destroy();
	return error;
}
